/*
 * $RCSfile: PrintCanvas3D.java,v $
 *
 * Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 *
 * $Revision: 1.2 $
 * $Date: 2006/03/06 21:11:48 $
 * $State: Exp $
 */

package com.turtle3d.gui.drawingpanel;


import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.URL;

import javax.media.j3d.AmbientLight;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Screen3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JFileChooser;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
import com.sun.j3d.utils.behaviors.mouse.MouseWheelZoom;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.turtle3d.App;
import com.turtle3d.exporter.ObjExporter;
import com.turtle3d.gui.ObjFileChooser;
import com.turtle3d.gui.visualoptions.VisualOptionsPanel;
import com.turtle3d.util.BranchBounds;


/**
* This code was edited or generated using CloudGarden's Jigloo
* SWT/Swing GUI Builder, which is free for non-commercial
* use. If Jigloo is being used commercially (ie, by a corporation,
* company or business for any purpose whatever) then you
* should purchase a license for each developer using Jigloo.
* Please visit www.cloudgarden.com for details.
* Use of Jigloo implies acceptance of these licensing terms.
* A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR
* THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED
* LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE.
*/
public class DrawingPanel extends JPanel implements PropertyChangeListener {

    private static final boolean noTriangulate = false;
    private static final boolean noStripify = false;
    private static final double creaseAngle = 60.0;
    private Canvas3D onScreenCanvas3D;
    private OffScreenCanvas3D offScreenCanvas3D;
    private URL filename = null;
    private static final int OFF_SCREEN_SCALE = 3;
    private static final double FOV = Math.PI/2.;
    
    private String recommendedFileName = "output";

    private SimpleUniverse univ = null;
    
    private TransformGroup objTrans;  
    private Background bgNode;
    
    public BranchGroup createSceneGraph() {
	// Create the root of the branch graph
	BranchGroup objRoot = new BranchGroup();
	
	// Create a Transformgroup to scale all objects so they
	// appear in the scene.
	TransformGroup objScale = new TransformGroup();
	Transform3D t3d = new Transform3D();
	t3d.setScale(0.7);
	objScale.setTransform(t3d);
	objRoot.addChild(objScale);	

	// Create the transform group node and initialize it to the
	// identity.  Enable the TRANSFORM_WRITE capability so that
	// our behavior code can modify it at runtime.  Add it to the
	// root of the subgraph.
	objTrans = new TransformGroup();
	objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
	objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
	objTrans.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
	objTrans.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
	objTrans.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);	
	
	//objTrans.setCapability(TransformGroup.ALLOW_
	//objTrans.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
	//objTrans.setCapability(TransformGroup.ALLOW_CHILDREN_READ);	
	objScale.addChild(objTrans);

	/*int flags = ObjectFile.RESIZE;
	if (!noTriangulate) flags |= ObjectFile.TRIANGULATE;
	if (!noStripify) flags |= ObjectFile.STRIPIFY;
	ObjectFile f =
	    new ObjectFile(flags, 
			   (float)(creaseAngle * Math.PI / 180.0));
	Scene scene = null;
	try {
	    scene = f.load(filename);
	}
	catch (FileNotFoundException e) {
	    System.err.println(e);
	    System.exit(1);
	}
	catch (ParsingErrorException e) {
	    System.err.println(e);
	    System.exit(1);
	}
	catch (IncorrectFormatException e) {
	    System.err.println(e);
	    System.exit(1);
	}
	  
	objTrans.addChild(scene.getSceneGroup());*/			

	BoundingSphere bounds =
	    new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

        // Create the rotate behavior node
        MouseRotate behavior = new MouseRotate();
        behavior.setTransformGroup(objTrans);
        objTrans.addChild(behavior);
        behavior.setSchedulingBounds(bounds);
        
        // Create the zoom behavior node
        MouseZoom behavior2 = new MouseZoom();
        behavior2.setTransformGroup(objTrans);
        objTrans.addChild(behavior2);
        behavior2.setSchedulingBounds(bounds);
        
        MouseWheelZoom behavior21 = new MouseWheelZoom();
        behavior21.setTransformGroup(objTrans);
        objTrans.addChild(behavior21);
        behavior21.setSchedulingBounds(bounds);
        
        // Create the translate behavior node
        MouseTranslate behavior3 = new MouseTranslate();
        behavior3.setTransformGroup(objTrans);
        objTrans.addChild(behavior3);
        behavior3.setSchedulingBounds(bounds);
	

	// Set up the background
	Color3f bgColor = new Color3f(0.5f, 0.5f, 1f);
	bgNode = new Background(bgColor);
	bgNode.setApplicationBounds(bounds);
	bgNode.setCapability(Background.ALLOW_COLOR_WRITE);
	objRoot.addChild(bgNode);

	// Set up the ambient light
	Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
	AmbientLight ambientLightNode = new AmbientLight(ambientColor);
	ambientLightNode.setInfluencingBounds(bounds);
	objRoot.addChild(ambientLightNode);

	// Set up the directional lights
	Color3f light1Color = new Color3f(1.0f, 1.0f, 0.9f);
	Vector3f light1Direction  = new Vector3f(4.0f, -7.0f, -12.0f);
	Color3f light2Color = new Color3f(0.3f, 0.3f, 0.4f);
	Vector3f light2Direction  = new Vector3f(-6.0f, -2.0f, -1.0f);

	DirectionalLight light1
	    = new DirectionalLight(light1Color, light1Direction);
	light1.setInfluencingBounds(bounds);
	objRoot.addChild(light1);

	DirectionalLight light2
	    = new DirectionalLight(light2Color, light2Direction);
	light2.setInfluencingBounds(bounds);
	objRoot.addChild(light2);

	return objRoot;
    }

    private void usage() {
	App.println("Usage: java PrintCanvas3D <.obj file>");
	System.exit(0);
    } // End of usage

    private OffScreenCanvas3D createOffScreenCanvas(Canvas3D onScreenCanvas3D) {
	// Create the off-screen Canvas3D object
	// request an offscreen Canvas3D with a single buffer configuration
	GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
	template.setDoubleBuffer(GraphicsConfigTemplate3D.UNNECESSARY);
	GraphicsConfiguration gc = 
                GraphicsEnvironment.getLocalGraphicsEnvironment().
                getDefaultScreenDevice().getBestConfiguration(template);

	offScreenCanvas3D = new OffScreenCanvas3D(gc, true);
	// Set the off-screen size based on a scale factor times the
	// on-screen size
	Screen3D sOn = onScreenCanvas3D.getScreen3D();
	Screen3D sOff = offScreenCanvas3D.getScreen3D();
	Dimension dim = sOn.getSize();
	dim.width *= OFF_SCREEN_SCALE;
	dim.height *= OFF_SCREEN_SCALE;
	sOff.setSize(dim);
	sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth() *
				    OFF_SCREEN_SCALE);
	sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight() *
				     OFF_SCREEN_SCALE);

	// attach the offscreen canvas to the view
	univ.getViewer().getView().addCanvas3D(offScreenCanvas3D);        

        return offScreenCanvas3D;
        
    }

    private Canvas3D createUniverse() {
	GraphicsConfiguration config =
	    SimpleUniverse.getPreferredConfiguration();

	Canvas3D c = new Canvas3D(config);

	univ = new SimpleUniverse(c);

	univ.getViewer().getView().setFieldOfView(FOV);
	// This will move the ViewPlatform back a bit so the
	// objects in the scene can be viewed.
        univ.getViewingPlatform().setNominalViewingTransform();

	// Ensure at least 5 msec per frame (i.e., < 200Hz)
	univ.getViewer().getView().setMinimumFrameCycleTime(5);
	
	return c;
    }

    /**
     * Creates new form PrintCanvas3D
     */
    public DrawingPanel() {
        init();
    }
    
    public void init(){
    	 // Initialize the GUI components
        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
        initComponents();        
      
        onScreenCanvas3D = createUniverse();
        this.add(onScreenCanvas3D, java.awt.BorderLayout.CENTER);               

        scene = createSceneGraph();              
                
        univ.addBranchGraph(scene);
        
        Transform3D trns = new Transform3D();
        trns.setTranslation(new Vector3f(0f,-2.5f,-5f));
        objTrans.setTransform(trns);  
    }
        
    private BranchGroup scene;
    
    private BranchGroup lastBranchGroup;
    
    public void drawScene(BranchGroup fractalObject){    	
    	fractalObject.setCapability(BranchGroup.ALLOW_DETACH);                                                              

        //delete old fractal
        if(lastBranchGroup!=null)
        	objTrans.removeChild(lastBranchGroup);
        lastBranchGroup = fractalObject;        
        lastBranchGroup.compile();
        
        objTrans.addChild(fractalObject);  
        
        
        
        //Create the off-screen Canvas3D object
        createOffScreenCanvas(onScreenCanvas3D);
                
    }

    // ----------------------------------------------------------------
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
    	
        this.setLayout(new java.awt.BorderLayout());
        
    }// </editor-fold>//GEN-END:initComponents
    
    public JMenuBar createMenu(){
       jMenuBar1 = new javax.swing.JMenuBar();
       fileMenu = new javax.swing.JMenu();
       snapShotMenuItem = new javax.swing.JMenuItem();
       exportMenuItem = new javax.swing.JMenuItem();
       exportAsMenuItem = new javax.swing.JMenuItem();
       printMenuItem = new javax.swing.JMenuItem();
       exitMenuItem = new javax.swing.JMenuItem();
     
       fileMenu.setText("File");
       snapShotMenuItem.setText("Snapshot");
       snapShotMenuItem.addActionListener(new java.awt.event.ActionListener() {
           public void actionPerformed(java.awt.event.ActionEvent evt) {
               snapShotMenuItemActionPerformed(evt);
           }
       });

       fileMenu.add(snapShotMenuItem);
       
       exportMenuItem.setText("Export");
       exportMenuItem.addActionListener(new ActionListener(){
       	public void actionPerformed(ActionEvent e)
       	{
       		try
				{
       			ObjExporter objExporter = new ObjExporter();        								        		
       			objExporter.add(lastBranchGroup);
       			objExporter.export(".", recommendedFileName);																				
				}
				catch (IOException e1)
				{
					App.printlnError("IO ERROR");
					e1.printStackTrace();
				}
				catch(NullPointerException e2){
					App.printlnError("No scene to export!");					
					e2.printStackTrace();
				}
       	}
       });
       fileMenu.add(exportMenuItem);
               
       exportAsMenuItem.setText("Export As");
       exportAsMenuItem.addActionListener(new ActionListener(){
       	public void actionPerformed(ActionEvent e)
       	{
       		try
				{
       			
       			ObjFileChooser fileChooser = new ObjFileChooser();
       			int returnVal = fileChooser.showSaveDialog((Component)e.getSource());
       			
       			if(returnVal == JFileChooser.APPROVE_OPTION){
       				ObjExporter objExporter = new ObjExporter();        								        		
           			objExporter.add(lastBranchGroup);
           			objExporter.export(fileChooser.getObjFilePath(), fileChooser.getObjFileName());
           			App.println("Scene exported");
       			}
				}
				catch (Exception e1)
				{
					App.println("IO ERROR");
					e1.printStackTrace();
				}
       	}
       });
       fileMenu.add(exportAsMenuItem);

//       printMenuItem.setText("Print");
//       printMenuItem.addActionListener(new java.awt.event.ActionListener() {
//           public void actionPerformed(java.awt.event.ActionEvent evt) {
//               printMenuItemActionPerformed(evt);
//           }
//       });
//
//       fileMenu.add(printMenuItem);

       exitMenuItem.setText("Exit");
       exitMenuItem.addActionListener(new java.awt.event.ActionListener() {
           public void actionPerformed(java.awt.event.ActionEvent evt) {
               exitMenuItemActionPerformed(evt);
           }
       });

       fileMenu.add(exitMenuItem);

       jMenuBar1.add(fileMenu);

       return jMenuBar1;		      
  
    }

    private void printMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_printMenuItemActionPerformed
	Point loc = onScreenCanvas3D.getLocationOnScreen();
	offScreenCanvas3D.setOffScreenLocation(loc);
	Dimension dim = onScreenCanvas3D.getSize();
	dim.width *= OFF_SCREEN_SCALE;
	dim.height *= OFF_SCREEN_SCALE;
	BufferedImage bImage =
	    offScreenCanvas3D.doRender(dim.width, dim.height);

        new ImagePrinter(bImage).print();

    }//GEN-LAST:event_printMenuItemActionPerformed

    private void snapShotMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_snapShotMenuItemActionPerformed
	Point loc = onScreenCanvas3D.getLocationOnScreen();
	offScreenCanvas3D.setOffScreenLocation(loc);
	Dimension dim = onScreenCanvas3D.getSize();
	dim.width *= OFF_SCREEN_SCALE;
	dim.height *= OFF_SCREEN_SCALE;
	BufferedImage bImage =
	    offScreenCanvas3D.doRender(dim.width, dim.height);

	new ImageDisplayer(bImage);


    }//GEN-LAST:event_snapShotMenuItemActionPerformed

    private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed
        System.exit(0);
    }//GEN-LAST:event_exitMenuItemActionPerformed
    
    // parameters declaration - do not modify//GEN-BEGIN:parameters
    private javax.swing.JPanel drawingPanel;
    private javax.swing.JMenuItem exitMenuItem;
    private javax.swing.JMenu fileMenu;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem printMenuItem;
    private javax.swing.JMenuItem snapShotMenuItem;
    private javax.swing.JMenuItem exportMenuItem;
    private javax.swing.JMenuItem exportAsMenuItem;
    // End of parameters declaration//GEN-END:parameters

    //private antialiasing = false;
	public void propertyChange(PropertyChangeEvent evt) {
		// TODO Auto-generated method stub
		if(evt.getPropertyName().equals(VisualOptionsPanel.AA))
			onScreenCanvas3D.getView().setSceneAntialiasingEnable((Boolean)evt.getNewValue());
		else if(evt.getPropertyName().equals(VisualOptionsPanel.BG_COLOR)){
			Color c = (Color)evt.getNewValue();
			bgNode.setColor(new Color3f(c.getRed()/255f, c.getGreen()/255f, c.getBlue()/255f));
		}
		else if(evt.getPropertyName().equals(VisualOptionsPanel.CROP)){
			cropView();
		}
	}
	
	private void cropView(){
		
		try {
			BranchBounds branchBounds = new BranchBounds(lastBranchGroup, objTrans);
			
			Point3f p = branchBounds.getMiddlePoint();			
			
			Transform3D transform = new Transform3D();		
			objTrans.getTransform(transform);

			transform.setTranslation(new Vector3f(-p.x, -p.y, -1.2f*branchBounds.calculateDistance((float)FOV)));
			
			objTrans.setTransform(transform);
		} catch (NullPointerException e) {
			App.printlnError("Can't crop empty scene!");
			//e.printStackTrace();
		}
							
	}

	public String getRecommendedFileName() {
		return recommendedFileName;
	}

	public void setRecommendedFileName(String recommendedFileName) {
		this.recommendedFileName = recommendedFileName;
	}	
    
}
